/* Testing helpers */

function test(name, func){
  print("Testing " + name + "...");
  func();
  print(".. ok");
};

function assertOk(func){
  // Shouldn't raise
  try{
    func();
  }catch(e){
    throw new String("<assertion failed>\n\tRaised when it shouldn't have.");
  }
}

function assert(condition, conditionString){
	if (!condition) {
		throw new String("<assertion failed> " + conditionString);
	}
}

function assertEq(a, b, conditionString){
  if((typeof(a)=="number") && (typeof(b)=="number")){
    // normalize floats
    a = Math.round(a * 1000000) / 1000000;
    b = Math.round(b * 1000000) / 1000000;
  }
	if (a!=b) {
		throw new String("<assertion failed> " + conditionString + "\n\tExpected <" + b + "> got <" + a + ">");
	}
}

function assertRaises(err, func, conditionString){
  var __x;
  try{func()}catch(e){__x=new String(e)};
  if(!__x){
		throw new String("<assertion failed> " + conditionString + "\n\tExpected to raise <" + err + ">, nothing raised.");
  }
  if(__x!=err){
		throw new String("<assertion failed> " + conditionString + "\n\tExpected to raise <" + err + ">, got <" + __x +">");
  }
}

/* Tests begin */

test("new Quaternion", function(){
  var q= new Quaternion;
  assertEq(new String(q), "[object Quaternion]");
  assertEq(new String(typeof(q.x)), "number");
  assertEq(q.w,1);

  q.x = 100;
  assertEq(q.x,100);
  
  // todo more tests (.yaw, etc)
  
  q.yaw(90);
});

test("new Vector", function(){
  var v = new Vector;
  assertEq(new String(v), "[object Vector]");
  assertEq(new String(typeof(v.x)), "number");
  assertEq(v.x, 0);
  v.x = 100;
  assertEq(v.x, 100);
  assertEq(v.inspect(), "Vector(100,0,0)");

  v.x = "blah";
  assertEq(new String(v.x), "NaN");
  
  var v2 = new Vector(1,2,3);
  assertEq(v2.x, 1);
  assertEq(v2.y,2);
  assertEq(v2.z,3);
});

test("vector.getDistance", function(){
  var v1 = new Vector,
    v2 = new Vector,
    node = world.createNode();
    
  v1.x = 100;
  v2.x = -50;
  
  assertEq(150, v1.getDistance(v2));
  
  assertRaises("Invalid argument", function(){v1.getDistance(0)});
  assertRaises("Invalid argument", function(){v1.getDistance("blah")});
  assertRaises("Invalid argument", function(){v1.getDistance(node)});
  
  node.destroy();
});

test("vector.getLength", function(){
  var v = new Vector;
    
  v.x = 10;
  v.y = 20;
  v.z = 30;
  
  assertEq(v.getLength(), 37.41657257080078);
});

test("vector.getNormalized", function(){
  var v = new Vector;
    
  v.x = 1;
  v.y = 2;
  v.z = 3;
  
  var v2 = v.getNormalized();

  assertEq(v2.getLength(), 1);
  assertEq(v2.x, 1.0 / 3.741657257080078);
});

test("world.createNode", function(){
  assertEq(new String(world), "[object World]");

  var node = world.createNode();
  assertEq(new String(node), "[object Node]");
  assert(node.id.match(/^.Node/));

  node.destroy();

});

test("Camera", function(){
  assertEq(new String(camera), "[object Camera]");
  
  camera.setPosition(new Vector(6,7,8));
  camera.lookAt(new Vector(0,0,100));

  var v = camera.getPosition();
  assertEq("Vector(6,7,8)", v.inspect());
});

test("OISInput", function(){
  assertEq(new String(oisinput), "[object OISInput]");
  assertEq(Keycodes.KC_W, 0x11);

  assertEq(false, oisinput.isKeyDown(Keycodes.KC_POWER));
  assertOk(function(){
    oisinput.mouseX;
    oisinput.mouseY;
    oisinput.mouseZ;
  });
});

//test("Node.getNearby", function(){
//  var v = new Vector;
//  assertRaises("Unimplemented", function(){world.getNearby(v)});
//});

test("Node.getPosition", function(){
  var n = world.createNode();
  assertEq(new String(n.getPosition()), "[object Vector]");
  assertEq(n.getPosition().x, 0);

  n.destroy();
});

test("Node.setPosition", function(){
  var node = world.createNode();
  var v = new Vector;
  v.x = 100;
  node.setPosition(v);
  assertEq(0, node.getPosition().getDistance(v));
  assertEq(node.getPosition().x, 100);
  assertRaises("Invalid argument", function(){node.setPosition("blah")});

  node.destroy();
});

test("Node.getRotation", function(){
  var n = world.createNode();
  assertEq(new String(n.getRotation()), "[object Quaternion]");
  assertEq(n.getRotation().x, 0);

  n.destroy();
});

test("Node.setRotation", function(){
  var n = world.createNode(),
    q = new Quaternion;
  q.yaw(10);
  n.setRotation(q);
  //todo more tests

  n.destroy();
});

test("Node.getScale", function(){
  var n = world.createNode();
  assertEq(new String(n.getScale()), "[object Vector]");
  
  // Doh! stupid PT_CUBE is too big
//  assertEq(n.getScale().x, 1);

  n.destroy();
});

test("Node.setScale", function(){
  var node = world.createNode();
  var v = new Vector;
  v.x=2;
  node.setScale(v);
  assertEq(node.getScale().x, 2);
  assertRaises("Invalid argument", function(){node.setScale(null)});

  node.destroy();
});

test("Events", function(){
  var node = world.createNode(),
    avatar = world.createNode();

  // Shouldn't raise
  assertOk(function(){
    Event.dispatch(node, 'touch', avatar);
  });

  // Sample of setting touch
  Event.observe(node, 'touch', function(self, sender){
    print(self.id + " touched by " + sender.id);
  });
  Event.dispatch(node, 'touch', avatar);

  // Sample of how to use the timer
  Event.observe(node, 'timer', function(self){
    print(self.id + " timered");

    /*
      var nodes = world.getNearby(self.getPosition());
      
      foreach(nodes, function(){
        print(this.getName() + " distance " + self.getPosition().getDistance(this.getPosition()) + " metres.");
      });
    */
  });
  Event.dispatch(node, 'timer', avatar);

  // Check remove works
  Event.remove(node, 'timer');
  Event.dispatch(node, 'timer', avatar);
  
  node.destroy();
  avatar.destroy();
  
});

"ok";